#include <SDL2/SDL.h>
#include <unordered_map>
#include <string>
#include "gl.hh"

namespace gl
{
	namespace
	{
		bool running;
		SDL_Window* win;
		std::unordered_map<std::string, bool> button_down;
		std::unordered_map<std::string, float> axis_;
	}

	void init()
	{
		SDL_Init(SDL_INIT_EVERYTHING);
		SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
		SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
		SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
		//SDL_GL_SetAttribute(SDL_GL_RED_SIZE  , 8);
		//SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE , 8);
		//SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
		//SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
		//SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
		win = SDL_CreateWindow("distance field demo",
						SDL_WINDOWPOS_CENTERED,
						SDL_WINDOWPOS_CENTERED,
						640, 360,
						SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
		SDL_GL_CreateContext(win);
		SDL_GL_SetSwapInterval(1);

		if (SDL_NumJoysticks()) SDL_JoystickOpen(0);
		running = true;
	}

	void quit()
	{
		running = false;
	}

	void run(render_func const& render, resize_func const& resize)
	{
		int w, h;
		SDL_GL_GetDrawableSize(win, &w, &h);
		resize(w, h);

		while (running) {
			SDL_Event e;
			while (SDL_PollEvent(&e)) {
				switch (e.type) {
					case SDL_QUIT: goto _quit;
					case SDL_KEYDOWN:
					case SDL_KEYUP: {
						bool down = (e.type == SDL_KEYDOWN);
						switch (e.key.keysym.sym) {
							case '\e': button_down["escape"] = down; break;
							case '\t': button_down["tab"] = down; break;
							default: {
								char name[2]{char(e.key.keysym.sym), 0};
								button_down[name] = down;
							}
						}
						break;
					}
					case SDL_JOYAXISMOTION: {
						auto name = "joy." + std::to_string(e.jaxis.axis);
						float x = float(e.jaxis.value + 0x8000) / 0xFFFF;
						x = x * 2 - 1;
						fprintf(stderr, ":: joy axis    %s = %f    ----\n", name.c_str(), x);
						axis_[name] = x;
						break;
					}
					case SDL_JOYBUTTONDOWN:
					case SDL_JOYBUTTONUP: {
						bool down = (e.type == SDL_JOYBUTTONDOWN);
						auto name = "joy." + std::to_string(e.jbutton.button);
						fprintf(stderr, ":: joy button  %s = %d    ----\n", name.c_str(), down);
						button_down[name] = down;
						break;
					}
					case SDL_JOYHATMOTION: {
						float x{};
						float y{};
						switch (e.jhat.value) {
							case SDL_HAT_UP   : y = +1; break;
							case SDL_HAT_DOWN : y = -1; break;
							case SDL_HAT_LEFT : x = -1; break;
							case SDL_HAT_RIGHT: x = +1; break;
							case SDL_HAT_LEFTUP   : x = -1; y = +1; break;
							case SDL_HAT_RIGHTUP  : x = +1; y = +1; break;
							case SDL_HAT_LEFTDOWN : x = -1; y = -1; break;
							case SDL_HAT_RIGHTDOWN: x = +1; y = -1; break;
						}
						auto prefix = "joy.hat." + std::to_string(e.jhat.hat);
						axis_[prefix + ".x"] = x;
						axis_[prefix + ".y"] = y;
						fprintf(stderr, ":: joy hat     %s = %+1.0f %+1.0f    ----\n", prefix.c_str(), x, y);
						break;
					}
					case SDL_WINDOWEVENT:
						if (e.window.event == SDL_WINDOWEVENT_RESIZED) {
							SDL_GL_GetDrawableSize(win, &w, &h);
							resize(w, h);
						}
						break;
				}
			}

			render(SDL_GetTicks() / 1000.0f);
			SDL_GL_SwapWindow(win);
		}

	_quit:
		SDL_Quit();
	}

	bool button(std::string const& name)
	{
		return button_down[name];
	}

	float axis(std::string const& name)
	{
		return axis_[name];
	}

	void axis(std::string const& name, float x)
	{
		axis_[name] = x;
	}
}
